﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace Inovout
{
    public static class TypeHelper
    {
        public static Type ExtractGenericInterface(Type queryType, Type interfaceType)
        {
            if (MatchesGenericType(queryType, interfaceType))
            {
                return queryType;
            }
            Type[] queryTypeInterfaces = queryType.GetInterfaces();
            return MatchGenericTypeFirstOrDefault(queryTypeInterfaces, interfaceType);
        }

        public static object GetDefaultValue(Type type)
        {
            return (TypeAllowsNullValue(type)) ? null : Activator.CreateInstance(type);
        }

        public static bool IsCompatibleObject<T>(object value)
        {
            return (value is T || (value == null && TypeAllowsNullValue(typeof(T))));
        }

        public static bool IsNullableValueType(Type type)
        {
            return Nullable.GetUnderlyingType(type) != null;
        }

        /// <summary>
        /// Provide a new <see cref="MissingMethodException"/> if original Message does not contain given full Type name.
        /// </summary>
        /// <param name="originalException"><see cref="MissingMethodException"/> to check.</param>
        /// <param name="fullTypeName">Full Type name which Message should contain.</param>
        /// <returns>New <see cref="MissingMethodException"/> if an update is required; null otherwise.</returns>
        public static MissingMethodException EnsureDebuggableException(
            MissingMethodException originalException,
            string fullTypeName)
        {
            MissingMethodException replacementException = null;
            if (!originalException.Message.Contains(fullTypeName))
            {
                string message = String.Format("CannotCreateInstance," + originalException.Message + ".." + fullTypeName);
                replacementException = new MissingMethodException(message, originalException);
            }

            return replacementException;
        }

        private static bool MatchesGenericType(Type type, Type matchType)
        {
            return type.IsGenericType && type.GetGenericTypeDefinition() == matchType;
        }

        private static Type MatchGenericTypeFirstOrDefault(Type[] types, Type matchType)
        {
            for (int i = 0; i < types.Length; i++)
            {
                Type type = types[i];
                if (MatchesGenericType(type, matchType))
                {
                    return type;
                }
            }
            return null;
        }

        private static bool StrongTryGetValueImpl<TKey, TValue>(object dictionary, string key, out object value)
        {
            IDictionary<TKey, TValue> strongDict = (IDictionary<TKey, TValue>)dictionary;

            TValue strongValue;
            bool retVal = strongDict.TryGetValue((TKey)(object)key, out strongValue);
            value = strongValue;
            return retVal;
        }

        private static bool TryGetValueFromNonGenericDictionary(object dictionary, string key, out object value)
        {
            IDictionary weakDict = (IDictionary)dictionary;

            bool containsKey = weakDict.Contains(key);
            value = (containsKey) ? weakDict[key] : null;
            return containsKey;
        }

        public static bool TypeAllowsNullValue(Type type)
        {
            return (!type.IsValueType || IsNullableValueType(type));
        }
        public static IDictionary ObjectToDictionary(object value)
        {
            IDictionary dictionary = new Dictionary<string,object>();

            if (value != null)
            {
                foreach (var property in value.GetType().GetProperties())
                {
                    dictionary.Add(property.Name, property.GetValue(value));
                }
            }

            return dictionary;
        }

    }
}
